<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Go语言并发编程 | anyang的博客</title>
    <meta name="description" content="千里之行,始于足下">
    <meta name="generator" content="VuePress 1.3.1">
    <link rel="icon" href="/img/favicon.ico">
  <script charset="utf-8" src="/js/main.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.slim.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.2/jquery.fancybox.min.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.2/jquery.fancybox.min.css">
  <meta name="keywords" content="福小林,奔奔,ourLang,CentOS,JAVA,vue组件,lsdCloud">
  <script async="async" src="https://www.googletagmanager.com/gtag/js?id=UA-146891701-1"></script>
  <script>window.dataLayer = window.dataLayer || [];
      function gtag(){dataLayer.push(arguments);}
      gtag('js', new Date());
    
      gtag('config', 'UA-146891701-1');</script>
  <script>var _hmt = _hmt || [];
      (function() {
        var hm = document.createElement("script");
        hm.src = "https://hm.baidu.com/hm.js?56eae8eec590ccaef1d5ff99d766f315";
        var s = document.getElementsByTagName("script")[0]; 
        s.parentNode.insertBefore(hm, s);
      })();</script>
    
    <link rel="preload" href="/assets/css/0.styles.9fff9873.css" as="style"><link rel="preload" href="/assets/js/app.1bc80adb.js" as="script"><link rel="preload" href="/assets/js/2.7c0608ab.js" as="script"><link rel="preload" href="/assets/js/82.7dac687f.js" as="script"><link rel="prefetch" href="/assets/js/10.81caa2ab.js"><link rel="prefetch" href="/assets/js/100.05bb269d.js"><link rel="prefetch" href="/assets/js/101.3e18b4d6.js"><link rel="prefetch" href="/assets/js/102.c7d7ed9c.js"><link rel="prefetch" href="/assets/js/103.4c771ce7.js"><link rel="prefetch" href="/assets/js/104.a2acd367.js"><link rel="prefetch" href="/assets/js/105.63399350.js"><link rel="prefetch" href="/assets/js/106.8a4744cc.js"><link rel="prefetch" href="/assets/js/107.7739d796.js"><link rel="prefetch" href="/assets/js/108.8008ae33.js"><link rel="prefetch" href="/assets/js/109.01c63898.js"><link rel="prefetch" href="/assets/js/11.7b837560.js"><link rel="prefetch" href="/assets/js/110.ad01aca9.js"><link rel="prefetch" href="/assets/js/111.3d74db70.js"><link rel="prefetch" href="/assets/js/112.67f55c34.js"><link rel="prefetch" href="/assets/js/113.016cd3c0.js"><link rel="prefetch" href="/assets/js/114.860e4b2b.js"><link rel="prefetch" href="/assets/js/115.b2f26258.js"><link rel="prefetch" href="/assets/js/116.8bea67b4.js"><link rel="prefetch" href="/assets/js/117.20682843.js"><link rel="prefetch" href="/assets/js/118.86a22e16.js"><link rel="prefetch" href="/assets/js/119.ad518bea.js"><link rel="prefetch" href="/assets/js/12.8fd78714.js"><link rel="prefetch" href="/assets/js/120.f97dd86f.js"><link rel="prefetch" href="/assets/js/121.c5601b4a.js"><link rel="prefetch" href="/assets/js/122.2889645a.js"><link rel="prefetch" href="/assets/js/13.ebf26820.js"><link rel="prefetch" href="/assets/js/14.b92dca09.js"><link rel="prefetch" href="/assets/js/15.b3dcef45.js"><link rel="prefetch" href="/assets/js/16.50378990.js"><link rel="prefetch" href="/assets/js/17.056bba2b.js"><link rel="prefetch" href="/assets/js/18.9047bb38.js"><link rel="prefetch" href="/assets/js/19.ab53b0fe.js"><link rel="prefetch" href="/assets/js/20.fe0b8af0.js"><link rel="prefetch" href="/assets/js/21.20a702f3.js"><link rel="prefetch" href="/assets/js/22.0ee08d29.js"><link rel="prefetch" href="/assets/js/23.02039a9a.js"><link rel="prefetch" href="/assets/js/24.363d3d74.js"><link rel="prefetch" href="/assets/js/25.dd6b101e.js"><link rel="prefetch" href="/assets/js/26.20493714.js"><link rel="prefetch" href="/assets/js/27.708723e5.js"><link rel="prefetch" href="/assets/js/28.4561834a.js"><link rel="prefetch" href="/assets/js/29.15cfcf5f.js"><link rel="prefetch" href="/assets/js/3.8d39d40c.js"><link rel="prefetch" href="/assets/js/30.04ab69de.js"><link rel="prefetch" href="/assets/js/31.b11c4302.js"><link rel="prefetch" href="/assets/js/32.81ae74ee.js"><link rel="prefetch" href="/assets/js/33.ddf09cc0.js"><link rel="prefetch" href="/assets/js/34.bc3549f7.js"><link rel="prefetch" href="/assets/js/35.4a0e0ebd.js"><link rel="prefetch" href="/assets/js/36.961a1c50.js"><link rel="prefetch" href="/assets/js/37.d8658de5.js"><link rel="prefetch" href="/assets/js/38.f76063aa.js"><link rel="prefetch" href="/assets/js/39.73edd08f.js"><link rel="prefetch" href="/assets/js/4.916af9bf.js"><link rel="prefetch" href="/assets/js/40.72314bff.js"><link rel="prefetch" href="/assets/js/41.c5eb1147.js"><link rel="prefetch" href="/assets/js/42.07aeafbb.js"><link rel="prefetch" href="/assets/js/43.c4a652ba.js"><link rel="prefetch" href="/assets/js/44.91fbe22b.js"><link rel="prefetch" href="/assets/js/45.6eecf0c4.js"><link rel="prefetch" href="/assets/js/46.81b7f108.js"><link rel="prefetch" href="/assets/js/47.b4468ab3.js"><link rel="prefetch" href="/assets/js/48.df205f23.js"><link rel="prefetch" href="/assets/js/49.7df59fa3.js"><link rel="prefetch" href="/assets/js/5.4fcd540a.js"><link rel="prefetch" href="/assets/js/50.0dc2fc5b.js"><link rel="prefetch" href="/assets/js/51.1ffabe54.js"><link rel="prefetch" href="/assets/js/52.2269d284.js"><link rel="prefetch" href="/assets/js/53.81b23843.js"><link rel="prefetch" href="/assets/js/54.b3249def.js"><link rel="prefetch" href="/assets/js/55.50dcf2af.js"><link rel="prefetch" href="/assets/js/56.c399c272.js"><link rel="prefetch" href="/assets/js/57.024f1dbb.js"><link rel="prefetch" href="/assets/js/58.680c6cf4.js"><link rel="prefetch" href="/assets/js/59.2291e9d7.js"><link rel="prefetch" href="/assets/js/6.7610d1d4.js"><link rel="prefetch" href="/assets/js/60.e59a26c1.js"><link rel="prefetch" href="/assets/js/61.ff5c64e4.js"><link rel="prefetch" href="/assets/js/62.7d97062c.js"><link rel="prefetch" href="/assets/js/63.6456dc7e.js"><link rel="prefetch" href="/assets/js/64.0b055680.js"><link rel="prefetch" href="/assets/js/65.9f25601c.js"><link rel="prefetch" href="/assets/js/66.cd973084.js"><link rel="prefetch" href="/assets/js/67.9b8f9716.js"><link rel="prefetch" href="/assets/js/68.d6c1869a.js"><link rel="prefetch" href="/assets/js/69.0a3688dd.js"><link rel="prefetch" href="/assets/js/7.9ebbd956.js"><link rel="prefetch" href="/assets/js/70.22f48891.js"><link rel="prefetch" href="/assets/js/71.9eb4fda9.js"><link rel="prefetch" href="/assets/js/72.ca4fa80c.js"><link rel="prefetch" href="/assets/js/73.17d1ab37.js"><link rel="prefetch" href="/assets/js/74.139691db.js"><link rel="prefetch" href="/assets/js/75.08d0560f.js"><link rel="prefetch" href="/assets/js/76.61078c10.js"><link rel="prefetch" href="/assets/js/77.4482adcc.js"><link rel="prefetch" href="/assets/js/78.166f2c37.js"><link rel="prefetch" href="/assets/js/79.2c7d6b79.js"><link rel="prefetch" href="/assets/js/8.6f8916c2.js"><link rel="prefetch" href="/assets/js/80.80445c3f.js"><link rel="prefetch" href="/assets/js/81.14e3bae8.js"><link rel="prefetch" href="/assets/js/83.1dd2e78e.js"><link rel="prefetch" href="/assets/js/84.23b759de.js"><link rel="prefetch" href="/assets/js/85.7574f69f.js"><link rel="prefetch" href="/assets/js/86.102dac61.js"><link rel="prefetch" href="/assets/js/87.8f5d639e.js"><link rel="prefetch" href="/assets/js/88.e564cbbe.js"><link rel="prefetch" href="/assets/js/89.cd66eb78.js"><link rel="prefetch" href="/assets/js/9.baa162db.js"><link rel="prefetch" href="/assets/js/90.685c6878.js"><link rel="prefetch" href="/assets/js/91.b8d1b7ea.js"><link rel="prefetch" href="/assets/js/92.1faa374d.js"><link rel="prefetch" href="/assets/js/93.cf5ff33c.js"><link rel="prefetch" href="/assets/js/94.8a02aefb.js"><link rel="prefetch" href="/assets/js/95.1b379a5b.js"><link rel="prefetch" href="/assets/js/96.9fd67185.js"><link rel="prefetch" href="/assets/js/97.c5a6a5a1.js"><link rel="prefetch" href="/assets/js/98.9a231411.js"><link rel="prefetch" href="/assets/js/99.5b344cab.js">
    <link rel="stylesheet" href="/assets/css/0.styles.9fff9873.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><!----> <span class="site-name">anyang的博客</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/" class="nav-link">
  主页
</a></div><div class="nav-item"><a href="/study-guide/introduction.html" class="nav-link">
  学习指南
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="技术分类" class="dropdown-title"><span class="title">技术分类</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/java/introduction.html" class="nav-link">
  Java
</a></li><li class="dropdown-item"><!----> <a href="/go/introduction.html" class="nav-link">
  Go
</a></li><li class="dropdown-item"><!----> <a href="/sql/introduction.html" class="nav-link">
  SQL
</a></li><li class="dropdown-item"><!----> <a href="/fhir/introduction.html" class="nav-link">
  FHIR
</a></li></ul></div></div><div class="nav-item"><a href="/blog/introduction.html" class="nav-link">
  个人博客
</a></div><div class="nav-item"><a href="/tool/developmentBox.html" class="nav-link">
  开发百宝箱
</a></div><div class="nav-item"><a href="/project/introduction.html" class="nav-link">
  项目分享
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="了解更多" class="dropdown-title"><span class="title">了解更多</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="http://wpa.qq.com/msgrd?v=3&amp;uin=1300378587&amp;site=qq&amp;menu=yes" target="_blank" rel="noopener noreferrer" class="nav-link external">
  其他合作
  <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li><li class="dropdown-item"><!----> <a href="http://www.lsdcloud.com/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  友情网站
  <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li><li class="dropdown-item"><!----> <a href="https://github.com/ourlang" target="_blank" rel="noopener noreferrer" class="nav-link external">
  Github
  <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li><li class="dropdown-item"><!----> <a href="https://gitee.com/anlexanyang" target="_blank" rel="noopener noreferrer" class="nav-link external">
  码云
  <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li><li class="dropdown-item"><!----> <a href="https://blog.csdn.net/qq_37493556" target="_blank" rel="noopener noreferrer" class="nav-link external">
  CSDN
  <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li></ul></div></div><div class="nav-item"><a href="/linkExchanges/introduction.html" class="nav-link">
  友情链接
</a></div> <!----></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/" class="nav-link">
  主页
</a></div><div class="nav-item"><a href="/study-guide/introduction.html" class="nav-link">
  学习指南
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="技术分类" class="dropdown-title"><span class="title">技术分类</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/java/introduction.html" class="nav-link">
  Java
</a></li><li class="dropdown-item"><!----> <a href="/go/introduction.html" class="nav-link">
  Go
</a></li><li class="dropdown-item"><!----> <a href="/sql/introduction.html" class="nav-link">
  SQL
</a></li><li class="dropdown-item"><!----> <a href="/fhir/introduction.html" class="nav-link">
  FHIR
</a></li></ul></div></div><div class="nav-item"><a href="/blog/introduction.html" class="nav-link">
  个人博客
</a></div><div class="nav-item"><a href="/tool/developmentBox.html" class="nav-link">
  开发百宝箱
</a></div><div class="nav-item"><a href="/project/introduction.html" class="nav-link">
  项目分享
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="了解更多" class="dropdown-title"><span class="title">了解更多</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="http://wpa.qq.com/msgrd?v=3&amp;uin=1300378587&amp;site=qq&amp;menu=yes" target="_blank" rel="noopener noreferrer" class="nav-link external">
  其他合作
  <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li><li class="dropdown-item"><!----> <a href="http://www.lsdcloud.com/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  友情网站
  <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li><li class="dropdown-item"><!----> <a href="https://github.com/ourlang" target="_blank" rel="noopener noreferrer" class="nav-link external">
  Github
  <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li><li class="dropdown-item"><!----> <a href="https://gitee.com/anlexanyang" target="_blank" rel="noopener noreferrer" class="nav-link external">
  码云
  <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li><li class="dropdown-item"><!----> <a href="https://blog.csdn.net/qq_37493556" target="_blank" rel="noopener noreferrer" class="nav-link external">
  CSDN
  <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li></ul></div></div><div class="nav-item"><a href="/linkExchanges/introduction.html" class="nav-link">
  友情链接
</a></div> <!----></nav>  <ul class="sidebar-links"><li><a href="/go/introduction.html" class="sidebar-link">Go语言</a></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading open"><span>Go教程</span> <span class="arrow down"></span></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/go/learn-go/10 从零搭建Go语言开发环境.html" class="sidebar-link">从零搭建Go语言开发环境</a></li><li><a href="/go/learn-go/11 第一个Go程序.html" class="sidebar-link">第一个Go程序Hello World</a></li><li><a href="/go/learn-go/12 Go 语言数据类型.html" class="sidebar-link">Go语言数据类型</a></li><li><a href="/go/learn-go/13 Go语言变量.html" class="sidebar-link">Go语言变量</a></li><li><a href="/go/learn-go/14 Go语言常量.html" class="sidebar-link">Go语言常量</a></li><li><a href="/go/learn-go/15 Go 语言运算符.html" class="sidebar-link">Go语言运算符</a></li><li><a href="/go/learn-go/16 Go语言条件语句.html" class="sidebar-link">Go语言条件语句</a></li><li><a href="/go/learn-go/17 Go语言的循环语句.html" class="sidebar-link">Go语言的循环语句</a></li><li><a href="/go/learn-go/18 Go 语言数组.html" class="sidebar-link">Go语言数组</a></li><li><a href="/go/learn-go/19 Go语言切片.html" class="sidebar-link">Go语言基础之切片</a></li><li><a href="/go/learn-go/20 Go语言map.html" class="sidebar-link">Go语言基础之map</a></li><li><a href="/go/learn-go/21 Go语言指针.html" class="sidebar-link">Go语言指针</a></li><li><a href="/go/learn-go/22 Go语言函数.html" class="sidebar-link">Go语言函数</a></li><li><a href="/go/learn-go/23  fmt.Printf总结.html" class="sidebar-link">fmt.Printf总结</a></li><li><a href="/go/learn-go/24 Go语言常用方法总结.html" class="sidebar-link">Go常用方法总结</a></li><li><a href="/go/learn-go/25 Go语言结构体.html" class="sidebar-link">Go语言结构体</a></li><li><a href="/go/learn-go/26 Go语言方法.html" class="sidebar-link">Go语言方法</a></li><li><a href="/go/learn-go/27 Go语言接口.html" class="sidebar-link">Go语言接口</a></li><li><a href="/go/learn-go/28 Go语言并发编程.html" class="active sidebar-link">Go语言并发编程</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_1-并发编程需要的基本概念" class="sidebar-link">1 并发编程需要的基本概念</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_1-1-什么是串行" class="sidebar-link">1.1 什么是串行?</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_1-2-什么是并行" class="sidebar-link">1.2 什么是并行?</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_1-3-什么是并发" class="sidebar-link">1.3 什么是并发?</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_1-4-并发与并行的区别" class="sidebar-link">1.4 并发与并行的区别</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_1-5-什么是程序" class="sidebar-link">1.5 什么是程序?</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_1-6-什么是进程" class="sidebar-link">1.6 什么是进程?</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_1-7-什么是线程" class="sidebar-link">1.7 什么是线程?</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_1-8-进程和线程总结" class="sidebar-link">1.8 进程和线程总结</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_1-9-什么是协程" class="sidebar-link">1.9 什么是协程?</a></li></ul></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_2-goroutine快速入门" class="sidebar-link">2 goroutine快速入门</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_2-1-使用goroutine" class="sidebar-link">2.1 使用goroutine</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_2-2-启动单个goroutine" class="sidebar-link">2.2 启动单个goroutine</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_2-3-启动多个goroutine" class="sidebar-link">2.3 启动多个goroutine</a></li></ul></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_3-gpm模型" class="sidebar-link">3 GPM模型</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_3-1-场景分析" class="sidebar-link">3.1 场景分析</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_3-2-正常情况下" class="sidebar-link">3.2 正常情况下</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_3-3-线程阻塞" class="sidebar-link">3.3 线程阻塞</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_3-4-runqueue执行完成" class="sidebar-link">3.4 runqueue执行完成</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_3-5-gmp简单举例" class="sidebar-link">3.5 GMP简单举例</a></li></ul></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_4-案例分析" class="sidebar-link">4 案例分析</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_5-channel" class="sidebar-link">5 channel</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_5-1-为什么需要channel" class="sidebar-link">5.1 为什么需要channel</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_5-2-并发模型是csp" class="sidebar-link">5.2 并发模型是CSP</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_5-3-channel类型定义" class="sidebar-link">5.3 channel类型定义</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_5-4-创建通道" class="sidebar-link">5.4 创建通道</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_5-5-channel操作" class="sidebar-link">5.5 channel操作</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_5-6-单向通道" class="sidebar-link">5.6 单向通道</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_5-7-channel简单举例" class="sidebar-link">5.7 channel简单举例</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_5-8-worker-pool（goroutine池）" class="sidebar-link">5.8 worker pool（goroutine池）</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_5-9-select多路复用" class="sidebar-link">5.9 select多路复用</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_5-10-channel总结" class="sidebar-link">5.10 channel总结</a></li></ul></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_6-sync-once" class="sidebar-link">6 sync.Once</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_6-1-sync-once源码" class="sidebar-link">6.1 sync.Once源码</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_6-2-do方法" class="sidebar-link">6.2 Do方法</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_6-3-举例一" class="sidebar-link">6.3 举例一</a></li></ul></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_7-sync-map" class="sidebar-link">7 sync.Map</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_7-1-sync-map的的整体结构" class="sidebar-link">7.1 sync.Map的的整体结构</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_7-2-sync-map的结构体说明" class="sidebar-link">7.2 sync.Map的结构体说明</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_7-3-sync-map常用操作函数" class="sidebar-link">7.3 sync.Map常用操作函数</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_7-4-操作函数源码解读" class="sidebar-link">7.4 操作函数源码解读</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_7-5-sync-map举例" class="sidebar-link">7.5 sync.Map举例</a></li></ul></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_8-原子操作" class="sidebar-link">8 原子操作</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_8-1-原子操作类型" class="sidebar-link">8.1 原子操作类型</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_8-2-有哪些原子操作" class="sidebar-link">8.2 有哪些原子操作</a></li><li class="sidebar-sub-header"><a href="/go/learn-go/28 Go语言并发编程.html#_8-3-原子操作示例" class="sidebar-link">8.3 原子操作示例</a></li></ul></li></ul></li><li><a href="/go/learn-go/29 Go语言文件操作.html" class="sidebar-link">Go语言文件操作</a></li><li><a href="/go/learn-go/30 Go语言单元测试.html" class="sidebar-link">Go语言单元测试</a></li><li><a href="/go/learn-go/31 Go语言之http.html" class="sidebar-link">Go语言之http</a></li></ul></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>Go Web框架</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>Go工具中间件</span> <span class="arrow right"></span></p> <!----></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="go语言并发编程"><a href="#go语言并发编程" class="header-anchor">#</a> Go语言并发编程</h1> <h2 id="_1-并发编程需要的基本概念"><a href="#_1-并发编程需要的基本概念" class="header-anchor">#</a> 1 并发编程需要的基本概念</h2> <div class="custom-block tip"><p class="custom-block-title">基本概念梳理</p> <ul><li>什么是串行?</li> <li>什么是并行?</li> <li>什么是并发?</li> <li>什么是程序?</li> <li>什么是进程?</li> <li>什么是线程?</li> <li>什么是协程?</li></ul></div> <h3 id="_1-1-什么是串行"><a href="#_1-1-什么是串行" class="header-anchor">#</a> 1.1 什么是串行?</h3> <blockquote><p>在计算机中, 同一时刻, 只能有一条指令, 在一个CPU上执行, 后面的指令必须等到前面指令执行完才能执行, 就是串行。串行就是按顺序执行, 就好比银行只有1个窗口, 有3个人要办事, 那么必须排队, 只有前面的人办完走人, 才能轮到你</p></blockquote> <p><a data-fancybox="" title="什么是串行" href="/img/goImage/goroutine1.png"><img src="/img/goImage/goroutine1.png" alt="什么是串行"></a></p> <h3 id="_1-2-什么是并行"><a href="#_1-2-什么是并行" class="header-anchor">#</a> 1.2 什么是并行?</h3> <blockquote><p>在计算机中, 同一时刻, 有多条指令, 在多个CPU上执行, 就是并行。并行就是同时执行, 就好比银行有3个窗口, 有3个人要办事, 只需要到空窗口即可立即办事.</p></blockquote> <p><a data-fancybox="" title="什么是并行" href="/img/goImage/goroutine2.png"><img src="/img/goImage/goroutine2.png" alt="什么是并行"></a></p> <h3 id="_1-3-什么是并发"><a href="#_1-3-什么是并发" class="header-anchor">#</a> 1.3 什么是并发?</h3> <blockquote><p>在计算机中, 同一时刻, 只能有一条指令, 在一个CPU上执行, 但是CPU会快速的在多条指令之间轮询执行就是并发。并发是伪并行, 就好比银行只有1个窗口, 有3个人要办事, 那么没轮到后面的人时, 后面的人可以用拖鞋先排队, 去吃个早餐,买个东西啥的, 感觉差不多要到自己时再回来办事</p></blockquote> <p><a data-fancybox="" title="什么是并发" href="/img/goImage/goroutine3.png"><img src="/img/goImage/goroutine3.png" alt="什么是并发"></a></p> <h3 id="_1-4-并发与并行的区别"><a href="#_1-4-并发与并行的区别" class="header-anchor">#</a> 1.4 并发与并行的区别</h3> <ul><li>多线程程序在单核上运行, 就是并发</li> <li>多线程程序在多核上运行,就是并行</li></ul> <p><a data-fancybox="" title="并发与并行的区别" href="/img/goImage/goroutine4.png"><img src="/img/goImage/goroutine4.png" alt="并发与并行的区别"></a></p> <h3 id="_1-5-什么是程序"><a href="#_1-5-什么是程序" class="header-anchor">#</a> 1.5 什么是程序?</h3> <blockquote><p>程序是指编译之后存储在磁盘上的一个二进制文件, 会占用磁盘空间, 但不会占用系统资源</p></blockquote> <h3 id="_1-6-什么是进程"><a href="#_1-6-什么是进程" class="header-anchor">#</a> 1.6 什么是进程?</h3> <blockquote><p>进程是指程序在操作系统中的一次执行过程, 是系统进行资源分配和调度的基本单位,举例如下</p></blockquote> <ul><li>启动记事本这个程序, 在系统中就会创建一个记事本进程</li> <li>再次启动记事本这个程序, 又会在系统中创建一个记事本进程</li></ul> <h3 id="_1-7-什么是线程"><a href="#_1-7-什么是线程" class="header-anchor">#</a> 1.7 什么是线程?</h3> <blockquote><p>线程是指进程中的一个执行实例, 是程序执行的最小单元, 它是比进程更小的能独立运行的基本单位.举例如下</p></blockquote> <ul><li>启动迅雷这个<code>程序</code>, 系统会创建一个<code>迅雷进程</code>, 并且默认会有一个<code>主线程</code>, 用于执行迅雷默认的业务逻辑</li> <li>当我们利用迅雷下载<code>多个任务</code>的时候, 会发现多个任务都在<code>同时下载</code>, 此时为了能够<code>同时执行</code>下载操作, 迅雷就会创建多个线程, 将不同的下载任务放到不同的线程中执行</li></ul> <h3 id="_1-8-进程和线程总结"><a href="#_1-8-进程和线程总结" class="header-anchor">#</a> 1.8 进程和线程总结</h3> <ul><li>进程就是程序程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位</li> <li>线程是进程的一个执行实例,是程序执行的最小单元,它是比进程更小的能独立运行的基本单位。</li> <li>一个进程可以创建核销毁多个线程,同一个进程中的多个线程可以并发执行</li> <li>一个程序至少有一个进程,一个进程至少有一个线程</li></ul> <h3 id="_1-9-什么是协程"><a href="#_1-9-什么是协程" class="header-anchor">#</a> 1.9 什么是协程?</h3> <ul><li>协程是一种用户态的轻量级线程，又称微线程，英文名Coroutine</li> <li>与传统的系统级别进程和线程相比, 协程最大的优势在于&quot;轻量级&quot;. 可以轻松创建上万个不会导致系统资源衰竭. 而线程和进程通常很难超过1万个.这也是协程称之为&quot;轻量级线程&quot;的原因</li> <li>一个线程中可以有任意多个协程, 但某一时刻只能有一个协程在运行, 多个协程分享所在线程分配到的计算机资源</li> <li>在协程中, 调用一个任务就像调用一个函数一样, 消耗系统资源极少, 但能达到进程、线程相同的并发效果</li></ul> <p><a data-fancybox="" title="什么是协程" href="/img/goImage/goroutine5.png"><img src="/img/goImage/goroutine5.png" alt="什么是协程"></a></p> <h2 id="_2-goroutine快速入门"><a href="#_2-goroutine快速入门" class="header-anchor">#</a> 2 goroutine快速入门</h2> <div class="custom-block danger"><p class="custom-block-title">goroutine特点</p> <ul><li>与传统的系统级线程和进程相比，协程的大优势在于其“轻量级”，可以轻松创建上百万个而不会导致系统资源衰竭，而线程和进程通常多也不能超过1万个。这也是协程也叫轻量级线程的原因。</li> <li>golang原生支持并发编程</li> <li>轻量级线程</li> <li>非抢占式多任务处理，由协程主动交出控制权</li> <li>编译器/解释器/虚拟机层面的多任务</li> <li>多个协程可能在一个或多个线程上运行</li></ul></div> <blockquote><p>Go主线程(有程序员直接称为线程/也可以理解成进程:一个Go线程上,可以起多个协程,你可以这样理解,协程是轻量的线程编译器做优化</p></blockquote> <p><a data-fancybox="" title="Go主线程" href="/img/goImage/goroutine6.png"><img src="/img/goImage/goroutine6.png" alt="Go主线程"></a></p> <h3 id="_2-1-使用goroutine"><a href="#_2-1-使用goroutine" class="header-anchor">#</a> 2.1 使用goroutine</h3> <blockquote><p>Go语言中使用<code>goroutine</code>非常简单，只需要在调用函数的时候在前面加上<code>go</code>关键字，就可以为一个函数创建一个<code>goroutine</code>。
一个<code>goroutine</code>必定对应一个函数，可以创建多个<code>goroutine</code>去执行相同的函数</p></blockquote> <h3 id="_2-2-启动单个goroutine"><a href="#_2-2-启动单个goroutine" class="header-anchor">#</a> 2.2 启动单个goroutine</h3> <blockquote><p>启动goroutine的方式非常简单，只需要在调用的函数（普通函数和匿名函数）前面加上一个go关键字</p></blockquote> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token operator">-</span> goroutine<span class="token operator">--</span>Go对协程的实现
<span class="token operator">-</span> <span class="token keyword">go</span> <span class="token operator">+</span> 函数名：启动一个协程执行函数
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p><strong>举例如下</strong></p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
	<span class="token string">&quot;fmt&quot;</span>
	<span class="token string">&quot;time&quot;</span>
<span class="token punctuation">)</span>
<span class="token comment">//定义一个函数helloGoroutine</span>
<span class="token keyword">func</span> <span class="token function">helloGoroutine</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">&quot;helloGoroutine &quot;</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token comment">//启动一个协程执行函数</span>
	<span class="token keyword">go</span> <span class="token function">helloGoroutine</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">&quot;执行main函数&quot;</span><span class="token punctuation">)</span>
	<span class="token comment">//为避免并发执行后程序立即退出，先sleep 2秒</span>
	time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br></div></div><p>输出结果:</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code>执行main函数
helloGoroutine 
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><h3 id="_2-3-启动多个goroutine"><a href="#_2-3-启动多个goroutine" class="header-anchor">#</a> 2.3 启动多个goroutine</h3> <h4 id="启动多个goroutine-有名称的函数"><a href="#启动多个goroutine-有名称的函数" class="header-anchor">#</a> 启动多个goroutine+有名称的函数</h4> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
	<span class="token string">&quot;fmt&quot;</span>
	<span class="token string">&quot;time&quot;</span>
<span class="token punctuation">)</span>
<span class="token comment">//定义一个函数helloGoroutine</span>
<span class="token keyword">func</span> <span class="token function">helloGoroutine</span><span class="token punctuation">(</span>x <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">&quot;helloGoroutine &quot;</span><span class="token punctuation">,</span> x<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">1</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">10</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>
		<span class="token comment">//启动一个协程执行函数</span>
		<span class="token keyword">go</span> <span class="token function">helloGoroutine</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
	<span class="token comment">//为避免并发执行后程序立即退出，先sleep 2秒</span>
	time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br></div></div><p>执行的结果顺序都会有变化</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code>helloGoroutine  <span class="token number">2</span>
helloGoroutine  <span class="token number">3</span>
helloGoroutine  <span class="token number">4</span>
helloGoroutine  <span class="token number">1</span>
helloGoroutine  <span class="token number">7</span>
helloGoroutine  <span class="token number">6</span>
helloGoroutine  <span class="token number">5</span>
helloGoroutine  <span class="token number">8</span>
helloGoroutine  <span class="token number">9</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><h4 id="使用sync-waitgroup来实现goroutine的同步"><a href="#使用sync-waitgroup来实现goroutine的同步" class="header-anchor">#</a> 使用sync.WaitGroup来实现goroutine的同步</h4> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
	<span class="token string">&quot;fmt&quot;</span>
	<span class="token string">&quot;sync&quot;</span>
<span class="token punctuation">)</span>
<span class="token keyword">var</span> wg sync<span class="token punctuation">.</span>WaitGroup

<span class="token comment">//定义一个函数hello</span>
<span class="token keyword">func</span> <span class="token function">hello</span><span class="token punctuation">(</span>i <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">defer</span> wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// goroutine结束就登记-1</span>
	fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">&quot;Hello Goroutine!&quot;</span><span class="token punctuation">,</span> i<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>

	<span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">10</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>
		wg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">// 启动一个goroutine就登记+1</span>
		<span class="token comment">//启动一个协程执行函数</span>
		<span class="token keyword">go</span> <span class="token function">hello</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
	wg<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 等待所有登记的goroutine都结束</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br></div></div><h4 id="sync-waitgroup说明"><a href="#sync-waitgroup说明" class="header-anchor">#</a> sync.WaitGroup说明</h4> <p><a data-fancybox="" title="waitgroup" href="/img/goImage/waitgroup.png"><img src="/img/goImage/waitgroup.png" alt="waitgroup"></a></p> <h2 id="_3-gpm模型"><a href="#_3-gpm模型" class="header-anchor">#</a> 3 GPM模型</h2> <blockquote><p><code>GPM</code>是Go语言运行时（<code>runtime</code>）层面的实现，是go语言自己实现的一套调度系统。区别于操作系统调度OS线程。
<a href="https://www.cnblogs.com/sunsky303/p/9705727.html" target="_blank" rel="noopener noreferrer">了解更多<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></p></blockquote> <div class="custom-block tip"><p class="custom-block-title">解释GMP模型含义</p> <ul><li>M结构是Machine，系统线程，它由操作系统管理，goroutine就是跑在M之上的；M是一个很大的结构，里面维护小对象内存cache（mcache）、当前执行的goroutine、随机数发生器等等非常多的信息</li> <li>P结构是Processor，处理器，它的主要用途就是用来执行goroutine，它维护了一个goroutine队列，即runqueue。Processor的让我们从N:1调度到M:N调度的重要部分。</li> <li>G是goroutine实现的核心结构，它包含了栈，指令指针，以及其他对调度goroutine很重要的信息，例如其阻塞的channel。</li></ul></div> <blockquote><p><strong>备注</strong>：Processor的数量是在启动时被设置为环境变量GOMAXPROCS的值，或者通过运行时调度函数GOMAXPROCS()进行设置。Processor数量固定意味着任意时刻只有GOMAXPROCS个线程在运行着go代码</p></blockquote> <h3 id="_3-1-场景分析"><a href="#_3-1-场景分析" class="header-anchor">#</a> 3.1 场景分析</h3> <h4 id="我们分别用三角形，矩形和圆形表示machine-processor和goroutine。"><a href="#我们分别用三角形，矩形和圆形表示machine-processor和goroutine。" class="header-anchor">#</a> 我们分别用三角形，矩形和圆形表示Machine Processor和Goroutine。</h4> <p><a data-fancybox="" title="场景分析" href="/img/goImage/gmp1.png"><img src="/img/goImage/gmp1.png" alt="gmp1"></a></p> <h3 id="_3-2-正常情况下"><a href="#_3-2-正常情况下" class="header-anchor">#</a> 3.2 正常情况下</h3> <blockquote><p>所有的goroutine运行在同一个M系统线程中，每一个M系统线程维护一个Processor，任何时刻，一个Processor中只有一个goroutine，其他goroutine在runqueue中等待。一个goroutine运行完自己的时间片后，让出上下文，回到runqueue中。 多核处理器的场景下，为了运行goroutines，每个M系统线程会持有一个Processor。</p></blockquote> <p><a data-fancybox="" title="gmp2" href="/img/goImage/gmp2.png"><img src="/img/goImage/gmp2.png" alt="gmp2"></a></p> <blockquote><p>如果两个M都在一个CPU上运行，这就是并发；如果两个M在不同的CPU上运行，这就是并行。在正常情况下，scheduler（调度器）会按照上面的流程进行调度，当一个G（goroutine）的时间片结束后将P（Processor）分配给下一个G，但是线程会发生阻塞等情况，看一下goroutine对线程阻塞等的处理。</p></blockquote> <h3 id="_3-3-线程阻塞"><a href="#_3-3-线程阻塞" class="header-anchor">#</a> 3.3 线程阻塞</h3> <blockquote><p>当正在运行的goroutine（G0）阻塞的时候，例如进行系统调用，会再创建一个系统线程（M1)，当前的M0线程放弃了它的Processor（P），P转到新的线程中去运行。</p></blockquote> <p><a data-fancybox="" title="gmp3" href="/img/goImage/gmp3.png"><img src="/img/goImage/gmp3.png" alt="gmp3"></a></p> <h3 id="_3-4-runqueue执行完成"><a href="#_3-4-runqueue执行完成" class="header-anchor">#</a> 3.4 runqueue执行完成</h3> <blockquote><p>当其中一个Processor的runqueue为空，没有goroutine可以调度，它会从另外一个上下文偷取一半的goroutine。</p></blockquote> <p><a data-fancybox="" title="gmp4" href="/img/goImage/gmp4.png"><img src="/img/goImage/gmp4.png" alt="gmp4"></a></p> <blockquote><p>首先创建一个G对象，G对象保存到P本地队列或者是全局队列。P此时去唤醒一个M。P继续执行它的执行序。M寻找是否有空闲的P，如果有则将该G对象移动到它本身。接下来M执行一个调度循环(调用G对象-&gt;执行-&gt;清理线程→继续找新的Goroutine执行)。
M执行过程中，随时会发生上下文切换。当发生上线文切换时，需要对执行现场进行保护，以便下次被调度执行时进行现场恢复。Go调度器M的栈保存在G对象上，只需要将M所需要的寄存器(SP、PC等)保存到G对象上就可以实现现场保护。当这些寄存器数据被保护起来，就随时可以做上下文切换了，在中断之前把现场保存起来。如果此时G任务还没有执行完，M可以将任务重新丢到P的任务队列，等待下一次被调度执行。当再次被调度执行时，M通过访问G的vdsoSP、vdsoPC寄存器进行现场恢复(从上次中断位置继续执行)。</p></blockquote> <h3 id="_3-5-gmp简单举例"><a href="#_3-5-gmp简单举例" class="header-anchor">#</a> 3.5 GMP简单举例</h3> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
	<span class="token string">&quot;fmt&quot;</span>
	<span class="token string">&quot;runtime&quot;</span>
	<span class="token string">&quot;sync&quot;</span>
<span class="token punctuation">)</span>
<span class="token keyword">var</span> wg sync<span class="token punctuation">.</span>WaitGroup

<span class="token keyword">func</span>  <span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span>  <span class="token punctuation">{</span>
	<span class="token keyword">defer</span> wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// goroutine结束就登记-1</span>
	<span class="token keyword">for</span> i<span class="token operator">:=</span><span class="token number">0</span><span class="token punctuation">;</span>i<span class="token operator">&lt;</span><span class="token number">10</span> <span class="token punctuation">;</span>i<span class="token operator">++</span>  <span class="token punctuation">{</span>
		fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">&quot;A=&quot;</span><span class="token punctuation">,</span>i<span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>


<span class="token keyword">func</span>  <span class="token function">b</span><span class="token punctuation">(</span><span class="token punctuation">)</span>  <span class="token punctuation">{</span>
	<span class="token keyword">defer</span> wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// goroutine结束就登记-1</span>
	<span class="token keyword">for</span> i<span class="token operator">:=</span><span class="token number">0</span><span class="token punctuation">;</span>i<span class="token operator">&lt;</span><span class="token number">10</span> <span class="token punctuation">;</span>i<span class="token operator">++</span>  <span class="token punctuation">{</span>
		fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">&quot;B=&quot;</span><span class="token punctuation">,</span>i<span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	 <span class="token comment">// 获取本地机器的逻辑CPU个数</span>
	cpu <span class="token operator">:=</span> runtime<span class="token punctuation">.</span><span class="token function">NumCPU</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token comment">//设置可同时执行的最大CPU数</span>
	runtime<span class="token punctuation">.</span><span class="token function">GOMAXPROCS</span><span class="token punctuation">(</span>cpu<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span>
	<span class="token comment">// 启动一个goroutine就登记+1,这里启动两个goroutine</span>
	wg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span>
	<span class="token comment">//启动两个goroutine分别执行a()和b()</span>
	<span class="token keyword">go</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token keyword">go</span> <span class="token function">b</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	wg<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 等待所有登记的goroutine都结束</span>

<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br></div></div><p>运行结果如下：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code>B<span class="token operator">=</span> <span class="token number">0</span>
B<span class="token operator">=</span> <span class="token number">1</span>
B<span class="token operator">=</span> <span class="token number">2</span>
A<span class="token operator">=</span> <span class="token number">0</span>
A<span class="token operator">=</span> <span class="token number">1</span>
A<span class="token operator">=</span> <span class="token number">2</span>
A<span class="token operator">=</span> <span class="token number">3</span>
A<span class="token operator">=</span> <span class="token number">4</span>
A<span class="token operator">=</span> <span class="token number">5</span>
A<span class="token operator">=</span> <span class="token number">6</span>
A<span class="token operator">=</span> <span class="token number">7</span>
A<span class="token operator">=</span> <span class="token number">8</span>
A<span class="token operator">=</span> <span class="token number">9</span>
B<span class="token operator">=</span> <span class="token number">3</span>
B<span class="token operator">=</span> <span class="token number">4</span>
B<span class="token operator">=</span> <span class="token number">5</span>
B<span class="token operator">=</span> <span class="token number">6</span>
B<span class="token operator">=</span> <span class="token number">7</span>
B<span class="token operator">=</span> <span class="token number">8</span>
B<span class="token operator">=</span> <span class="token number">9</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br></div></div><h2 id="_4-案例分析"><a href="#_4-案例分析" class="header-anchor">#</a> 4 案例分析</h2> <p>计算1到20各个数的阶乘，并且把各个数的阶乘放到map中，最后显示出来，要求使用<code>goroutine</code>完成</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main
<span class="token keyword">import</span> <span class="token punctuation">(</span>
	<span class="token string">&quot;fmt&quot;</span>
<span class="token punctuation">)</span>
<span class="token comment">//1.map应该是全局的</span>
<span class="token keyword">var</span><span class="token punctuation">(</span>
	myMap <span class="token operator">=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">int</span><span class="token punctuation">]</span><span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">)</span>
<span class="token punctuation">)</span>

<span class="token comment">//test函数就是计算 n!，把结果放到myMap中</span>
<span class="token keyword">func</span> <span class="token function">test</span><span class="token punctuation">(</span>n <span class="token builtin">int</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
	res <span class="token operator">:=</span><span class="token number">1</span>
	<span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">1</span><span class="token punctuation">;</span> i <span class="token operator">&lt;=</span> n<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">{</span>
		res <span class="token operator">*=</span> i
	<span class="token punctuation">}</span>
	<span class="token comment">//把res放入到myMap</span>
	myMap<span class="token punctuation">[</span>n<span class="token punctuation">]</span> <span class="token operator">=</span> res
<span class="token punctuation">}</span>
<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token comment">//开启多个协程完成这个任务</span>
	<span class="token keyword">for</span> i<span class="token operator">:=</span><span class="token number">1</span><span class="token punctuation">;</span>i<span class="token operator">&lt;=</span><span class="token number">20</span><span class="token punctuation">;</span>i<span class="token operator">++</span><span class="token punctuation">{</span>
		<span class="token keyword">go</span> <span class="token function">test</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
	<span class="token comment">//time.Sleep(time.Second)</span>
	<span class="token keyword">for</span> i<span class="token punctuation">,</span>v<span class="token operator">:=</span> <span class="token keyword">range</span> myMap<span class="token punctuation">{</span>
		fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">&quot;阶乘：%d！=%d\n&quot;</span><span class="token punctuation">,</span>i<span class="token punctuation">,</span>v<span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br></div></div><h2 id="_5-channel"><a href="#_5-channel" class="header-anchor">#</a> 5 channel</h2> <h3 id="_5-1-为什么需要channel"><a href="#_5-1-为什么需要channel" class="header-anchor">#</a> 5.1 为什么需要channel</h3> <blockquote><p>单纯地将函数并发执行是没有意义的。函数与函数间需要交换数据才能体现并发执行函数的意义。
虽然可以使用共享内存进行数据交换，但是共享内存在不同的<code>goroutine</code>中容易发生竞态问题。为了保证数据交换的正确性，必须使用互斥量对内存进行加锁，这种做法势必造成性能问题。</p></blockquote> <h3 id="_5-2-并发模型是csp"><a href="#_5-2-并发模型是csp" class="header-anchor">#</a> 5.2 并发模型是CSP</h3> <div class="custom-block tip"><p class="custom-block-title">channel介绍</p> <p><code>channel</code>是<code>goroutine</code>进行通信的管道，数据从一端发送到另一端，通过通道接收。
Go语言的并发模型是CSP（Communicating Sequential Processes），<strong>提倡通过通信共享内存而不是通过共享内存而实现通信。</strong></p></div> <h3 id="_5-3-channel类型定义"><a href="#_5-3-channel类型定义" class="header-anchor">#</a> 5.3 channel类型定义</h3> <blockquote><p>Go 语言中的通道（<code>channel</code>）是一种特殊的类型。通道像一个传送带或者队列，总是遵循先入先出（<code>First In First Out</code>）的规则，保证收发数据的顺序。每一个通道都是一个具体类型的导管，也就是声明<code>channel</code>的时候需要为其指定元素类型。
channel是一种类型，一种引用类型。声明通道类型的格式如下</p></blockquote> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">var</span> 变量 <span class="token keyword">chan</span> 元素类型
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>举个栗子</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">var</span> ch1 <span class="token keyword">chan</span> <span class="token builtin">int</span>   <span class="token comment">// 声明一个传递整型的通道</span>
<span class="token keyword">var</span> ch2 <span class="token keyword">chan</span> <span class="token builtin">bool</span>  <span class="token comment">// 声明一个传递布尔型的通道</span>
<span class="token keyword">var</span> ch3 <span class="token keyword">chan</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">int</span> <span class="token comment">// 声明一个传递int切片的通道</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><h3 id="_5-4-创建通道"><a href="#_5-4-创建通道" class="header-anchor">#</a> 5.4 创建通道</h3> <blockquote><p>通道的零值为<code>nil</code>。<code>nil</code>通道没有意义，因此通道必须使用类似<code>map</code>和切片的方式来定义。创建<code>channel</code>的格式如下：<code>channel</code>的缓冲大小是可选的。</p></blockquote> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> 元素类型<span class="token punctuation">,</span> <span class="token punctuation">[</span>缓冲大小<span class="token punctuation">]</span><span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><h3 id="_5-5-channel操作"><a href="#_5-5-channel操作" class="header-anchor">#</a> 5.5 channel操作</h3> <blockquote><p>通道有发送（<code>send</code>）、接收(<code>receive</code>）和关闭（<code>close</code>）三种操作。
发送和接收都使用<code>&lt;-</code>符号。</p></blockquote> <h4 id="_5-5-1-定义一个举例channel"><a href="#_5-5-1-定义一个举例channel" class="header-anchor">#</a> 5.5.1 定义一个举例channel</h4> <div class="language-go line-numbers-mode"><pre class="language-go"><code>ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span><span class="token number">10</span><span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><h4 id="_5-5-2-发送"><a href="#_5-5-2-发送" class="header-anchor">#</a> 5.5.2 发送</h4> <blockquote><p>将一个值发送到通道中</p></blockquote> <div class="language-go line-numbers-mode"><pre class="language-go"><code>ch <span class="token operator">&lt;-</span> <span class="token number">10</span> <span class="token comment">// 把10发送到通道ch中</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><h4 id="_5-5-3-接收"><a href="#_5-5-3-接收" class="header-anchor">#</a> 5.5.3 接收</h4> <blockquote><p>从一个通道中接收值。</p></blockquote> <div class="language-go line-numbers-mode"><pre class="language-go"><code> x <span class="token operator">:=</span> <span class="token operator">&lt;-</span> ch <span class="token comment">// 从通道ch中接收值并赋值给变量x</span>
  <span class="token operator">&lt;-</span>ch       <span class="token comment">// 从通道ch中接收值，忽略结果     </span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><h4 id="_5-5-4-关闭"><a href="#_5-5-4-关闭" class="header-anchor">#</a> 5.5.4 关闭</h4> <blockquote><p>我们通过调用内置的close函数来关闭通道。关于关闭通道需要注意的事情是，只有在通知接收方<code>goroutine</code>所有的数据都发送完毕的时候才需要关闭通道。通道是可以被垃圾回收机制回收的，它和关闭文件是不一样的，在结束操作之后关闭文件是必须要做的，但关闭通道不是必须的。</p></blockquote> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token function">close</span><span class="token punctuation">(</span>ch<span class="token punctuation">)</span>          
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><h4 id="_5-5-5-关闭后的通道特点"><a href="#_5-5-5-关闭后的通道特点" class="header-anchor">#</a> 5.5.5 关闭后的通道特点</h4> <ul><li>对一个关闭的通道再发送值就会导致panic。</li> <li>对一个关闭的通道进行接收会一直获取值直到通道为空。</li> <li>对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。</li> <li>关闭一个已经关闭的通道会导致panic。</li></ul> <h3 id="_5-6-单向通道"><a href="#_5-6-单向通道" class="header-anchor">#</a> 5.6 单向通道</h3> <blockquote><p>Go语言的类型系统提供了单方向的<code>channel</code> 类型，顾名思义，单向<code>channel</code> 只能用于发送或者接收数据。<code>channel</code> 本身必然是同时支持读写的，否则根本没法用。</p></blockquote> <h4 id="_5-6-1-单向通道的声明格式"><a href="#_5-6-1-单向通道的声明格式" class="header-anchor">#</a> 5.6.1 单向通道的声明格式</h4> <blockquote><p>我们在将一个<code>channel</code> 变量传递到一个函数时，可以通过将其指定为单向<code>channel</code> 变量，从而限制该函数中可以对此<code>channel</code> 的操作，比如只能往这个<code>channel</code> 写，或者只能从这个<code>channel</code> 读。
单向<code>channel</code> 变量的声明非常简单，只能发送的通道类型为<code>chan&lt;-</code>，只能接收的通道类型为<code>&lt;-chan</code>，格式如下：</p></blockquote> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">var</span> 通道实例 <span class="token keyword">chan</span><span class="token operator">&lt;-</span> 元素类型    <span class="token comment">// 只能发送通道</span>
<span class="token keyword">var</span> 通道实例 <span class="token operator">&lt;-</span><span class="token keyword">chan</span> 元素类型    <span class="token comment">// 只能接收通道</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><ul><li>元素类型：通道包含的元素类型。</li> <li>通道实例：声明的通道变量。</li></ul> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">var</span> ch1 <span class="token keyword">chan</span> <span class="token builtin">int</span> 　　　　　　     <span class="token comment">//ch1是一个正常的channel，不是单向的</span>
<span class="token keyword">var</span> ch2 <span class="token keyword">chan</span> <span class="token operator">&lt;-</span> <span class="token builtin">float64</span> 　　     <span class="token comment">//ch2是一个单向的channel，只用于写float64的数据</span>
<span class="token keyword">var</span> ch3  <span class="token operator">&lt;-</span> <span class="token keyword">chan</span>  <span class="token builtin">int</span>　　　　    <span class="token comment">//ch3是一个单向的channel，只用于读取int数据</span>

ch4 <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span><span class="token number">10</span><span class="token punctuation">)</span>         <span class="token comment">//定义并初始化普通通道</span>
send <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span><span class="token operator">&lt;-</span> <span class="token builtin">int</span><span class="token punctuation">,</span><span class="token number">10</span><span class="token punctuation">)</span>      <span class="token comment">//定义并初始化一个仅仅是发送通道</span>
receive <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span> <span class="token operator">&lt;-</span><span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token punctuation">,</span><span class="token number">10</span><span class="token punctuation">)</span> <span class="token comment">//定义并初始化一个仅仅是接收通道</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><h4 id="_5-6-2-常见错误举例"><a href="#_5-6-2-常见错误举例" class="header-anchor">#</a> 5.6.2 常见错误举例</h4> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token comment">//创建一个channel，双向的</span>
	ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span>
	<span class="token comment">//定义一个单向的只能写的channel</span>
	<span class="token keyword">var</span> writech <span class="token keyword">chan</span> <span class="token operator">&lt;-</span> <span class="token builtin">int</span> <span class="token operator">=</span> ch
	<span class="token comment">//但如果写成下面这样就会报错</span>
	<span class="token operator">&lt;-</span>writech
	<span class="token comment">//定义一个单向的只能读的channel</span>
	<span class="token keyword">var</span> readch <span class="token operator">&lt;-</span> <span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token operator">=</span> ch
	<span class="token comment">//写成下面这样就会有问题</span>
	readch<span class="token operator">&lt;-</span><span class="token number">555</span>
	<span class="token comment">//下面都会正常编译通过</span>
	writech <span class="token operator">&lt;-</span> <span class="token number">666</span>
	<span class="token operator">&lt;-</span> readch
	<span class="token comment">//单向无法转换成双向</span>
	<span class="token keyword">var</span> ch2  <span class="token keyword">chan</span> <span class="token builtin">int</span> <span class="token operator">=</span> writech
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br></div></div><h4 id="_5-6-3-生产者-消费者"><a href="#_5-6-3-生产者-消费者" class="header-anchor">#</a> 5.6.3 生产者-消费者</h4> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token string">&quot;fmt&quot;</span>

<span class="token comment">//代表只能往channel里面发送消息，不能接收</span>
<span class="token keyword">func</span> <span class="token function">producer</span><span class="token punctuation">(</span>out <span class="token keyword">chan</span><span class="token operator">&lt;-</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token comment">//循环把i的平方发送到通道out里面</span>
	<span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;=</span> <span class="token number">10</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>
		out <span class="token operator">&lt;-</span> i <span class="token operator">*</span> i
	<span class="token punctuation">}</span>
	<span class="token comment">//关闭通道</span>
	<span class="token function">close</span><span class="token punctuation">(</span>out<span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token comment">//代表只能往channel里面接收消息，不能发送</span>
<span class="token keyword">func</span> <span class="token function">consumer</span><span class="token punctuation">(</span>in <span class="token operator">&lt;-</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token comment">//循环读取通道in的数据</span>
	<span class="token keyword">for</span> num <span class="token operator">:=</span> <span class="token keyword">range</span> in <span class="token punctuation">{</span>
		fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">&quot;num = &quot;</span><span class="token punctuation">,</span> num<span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token comment">//创建一个双向通道ch</span>
	ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span>
	<span class="token comment">//生产者，生产数字，写入channel</span>
	<span class="token keyword">go</span> <span class="token function">producer</span><span class="token punctuation">(</span>ch<span class="token punctuation">)</span> <span class="token comment">//channel传参，引用传递</span>
	<span class="token comment">//消费者，从channel里读取数字、然后打印</span>
	<span class="token function">consumer</span><span class="token punctuation">(</span>ch<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br></div></div><p>输出结果如下：</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code>num <span class="token operator">=</span>  <span class="token number">0</span>
num <span class="token operator">=</span>  <span class="token number">1</span>
num <span class="token operator">=</span>  <span class="token number">4</span>
num <span class="token operator">=</span>  <span class="token number">9</span>
num <span class="token operator">=</span>  <span class="token number">16</span>
num <span class="token operator">=</span>  <span class="token number">25</span>
num <span class="token operator">=</span>  <span class="token number">36</span>
num <span class="token operator">=</span>  <span class="token number">49</span>
num <span class="token operator">=</span>  <span class="token number">64</span>
num <span class="token operator">=</span>  <span class="token number">81</span>
num <span class="token operator">=</span>  <span class="token number">100</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><h3 id="_5-7-channel简单举例"><a href="#_5-7-channel简单举例" class="header-anchor">#</a> 5.7 channel简单举例</h3> <h4 id="例子一"><a href="#例子一" class="header-anchor">#</a> 例子一</h4> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token string">&quot;fmt&quot;</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>

	ch1<span class="token operator">:=</span><span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span><span class="token number">10</span><span class="token punctuation">)</span> <span class="token comment">//定义一个有缓冲区的int通道</span>
	ch1<span class="token operator">&lt;-</span><span class="token number">10</span> <span class="token comment">//把10发送到通道ch1中</span>
	fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>ch1<span class="token punctuation">)</span> <span class="token comment">// 0xc000082000</span>

	x<span class="token operator">:=</span><span class="token operator">&lt;-</span>ch1 <span class="token comment">//变量x从通道ch1接收值</span>
	fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">&quot;变量x从通道ch1取出的值是&quot;</span><span class="token punctuation">,</span>x<span class="token punctuation">)</span> <span class="token comment">//变量x从通道ch1取出的值是 10</span>

    <span class="token function">close</span><span class="token punctuation">(</span>ch1<span class="token punctuation">)</span> <span class="token comment">//关闭通道</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br></div></div><h4 id="例子二"><a href="#例子二" class="header-anchor">#</a> 例子二</h4> <blockquote><ul><li>启动一个<code>goroutine</code>，生成100个数发送到ch1中</li> <li>启动一个<code>goroutine</code>,从ch1中取值，计算其平方放到ch2中</li> <li>在main函数中，打印ch2里面的值</li></ul></blockquote> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
	<span class="token string">&quot;fmt&quot;</span>
	<span class="token string">&quot;sync&quot;</span>
<span class="token punctuation">)</span>

<span class="token keyword">var</span> <span class="token punctuation">(</span>
	ch1 <span class="token operator">=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span> <span class="token comment">//声明两个缓冲区为100的管道</span>
	ch2 <span class="token operator">=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span>
	wg  sync<span class="token punctuation">.</span>WaitGroup <span class="token comment">//用于等待一组协程goroutine的结束</span>
<span class="token punctuation">)</span>

<span class="token comment">//生成100个数把它发送到ch1</span>
<span class="token keyword">func</span> <span class="token function">write</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">defer</span> wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">100</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>
		ch1 <span class="token operator">&lt;-</span> i
	<span class="token punctuation">}</span>
	<span class="token function">close</span><span class="token punctuation">(</span>ch1<span class="token punctuation">)</span> <span class="token comment">//关闭通道 如果没有close 会报错goroutine XX [chan receive]:</span>
<span class="token punctuation">}</span>

<span class="token comment">//从ch1取值，然后计算平方到ch2</span>
<span class="token keyword">func</span> <span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">defer</span> wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// goroutine结束就登记-1</span>
	<span class="token keyword">for</span> <span class="token punctuation">{</span>
		x<span class="token punctuation">,</span> ok <span class="token operator">:=</span> <span class="token operator">&lt;-</span>ch1
		<span class="token keyword">if</span> <span class="token operator">!</span>ok <span class="token punctuation">{</span> <span class="token comment">//判断channel是否关闭,关闭了退出for循环</span>
			<span class="token keyword">break</span>
		<span class="token punctuation">}</span>
		val <span class="token operator">:=</span> x <span class="token operator">*</span> x <span class="token comment">//如果没有退出for循环 计算其平方值放入ch2中</span>
		ch2 <span class="token operator">&lt;-</span> val
	<span class="token punctuation">}</span>
	<span class="token function">close</span><span class="token punctuation">(</span>ch2<span class="token punctuation">)</span>  <span class="token comment">//关闭通道 如果没有close 会报错goroutine XX [chan receive]:</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	wg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span> <span class="token comment">// 启动两个goroutine登记+2</span>
	<span class="token keyword">go</span> <span class="token function">write</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 	<span class="token comment">//启动一个协程执行函数write()</span>
	<span class="token keyword">go</span> <span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span>	<span class="token comment">//启动一个协程执行函数read()</span>
	wg<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 等待所有登记的goroutine都结束</span>
	<span class="token comment">//使用for range读通道ch2中的值</span>
	<span class="token keyword">for</span> x <span class="token operator">:=</span> <span class="token keyword">range</span> ch2 <span class="token punctuation">{</span>
		fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">&quot;读取到的值为%d\n&quot;</span><span class="token punctuation">,</span> x<span class="token punctuation">)</span> <span class="token comment">//打印输出</span>
	<span class="token punctuation">}</span>

<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br></div></div><h3 id="_5-8-worker-pool（goroutine池）"><a href="#_5-8-worker-pool（goroutine池）" class="header-anchor">#</a> 5.8 worker pool（goroutine池）</h3> <blockquote><p>提供一个<code>goroutine</code>池，每个 <code>goroutine</code> 循环阻塞等待从任务池中执行任务；外界使用者不断的往任务池里丢任务，则 <code>goroutine</code>池中的多个 <code>goroutine</code> 会并发的处理这些任务
<a href="https://mp.weixin.qq.com/s/aoWQSxrXXZUtJh48tmqMkA" target="_blank" rel="noopener noreferrer">了解更多goroutine池<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></p></blockquote> <h4 id="_5-8-1-举例一"><a href="#_5-8-1-举例一" class="header-anchor">#</a> 5.8.1 举例一</h4> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
	<span class="token string">&quot;fmt&quot;</span>
	<span class="token string">&quot;sync&quot;</span>
	<span class="token string">&quot;time&quot;</span>
<span class="token punctuation">)</span>

<span class="token keyword">var</span> wg sync<span class="token punctuation">.</span>WaitGroup
<span class="token keyword">func</span> <span class="token function">worker</span><span class="token punctuation">(</span>id <span class="token builtin">int</span><span class="token punctuation">,</span> jobs <span class="token operator">&lt;-</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span> results <span class="token keyword">chan</span><span class="token operator">&lt;-</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">defer</span> wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token keyword">for</span> j <span class="token operator">:=</span> <span class="token keyword">range</span> jobs <span class="token punctuation">{</span>
		fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">&quot;worker:%d 开始执行 job:%d\n&quot;</span><span class="token punctuation">,</span> id<span class="token punctuation">,</span> j<span class="token punctuation">)</span>
		time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span>
		fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">&quot;worker:%d 执行结束 job:%d\n&quot;</span><span class="token punctuation">,</span> id<span class="token punctuation">,</span> j<span class="token punctuation">)</span>
		results <span class="token operator">&lt;-</span> j <span class="token operator">*</span> <span class="token number">2</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	jobs <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span>
	results <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span>
	<span class="token comment">// 开启3个goroutine</span>
	wg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span>
	<span class="token keyword">for</span> w <span class="token operator">:=</span> <span class="token number">1</span><span class="token punctuation">;</span> w <span class="token operator">&lt;=</span> <span class="token number">3</span><span class="token punctuation">;</span> w<span class="token operator">++</span> <span class="token punctuation">{</span>
		<span class="token keyword">go</span> <span class="token function">worker</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> jobs<span class="token punctuation">,</span> results<span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
	<span class="token comment">// 5个任务</span>
	<span class="token keyword">for</span> j <span class="token operator">:=</span> <span class="token number">1</span><span class="token punctuation">;</span> j <span class="token operator">&lt;=</span> <span class="token number">5</span><span class="token punctuation">;</span> j<span class="token operator">++</span> <span class="token punctuation">{</span>
		jobs <span class="token operator">&lt;-</span> j
	<span class="token punctuation">}</span>
	<span class="token function">close</span><span class="token punctuation">(</span>jobs<span class="token punctuation">)</span>
	<span class="token comment">// 输出结果</span>
	wg<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br></div></div><h4 id="_5-8-1-举例二"><a href="#_5-8-1-举例二" class="header-anchor">#</a> 5.8.1 举例二</h4> <blockquote><p>使用goroutine和channel实现一个计算int64随机数各位数和的程序。</p></blockquote> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
	<span class="token string">&quot;fmt&quot;</span>
	<span class="token string">&quot;math/rand&quot;</span>
	<span class="token string">&quot;sync&quot;</span>
	<span class="token string">&quot;time&quot;</span>
<span class="token punctuation">)</span>

<span class="token keyword">var</span> wg sync<span class="token punctuation">.</span>WaitGroup

<span class="token comment">//计算一个64位随机数的各位的和</span>
<span class="token keyword">func</span> <span class="token function">randNumber</span><span class="token punctuation">(</span>x <span class="token builtin">int64</span><span class="token punctuation">)</span> <span class="token builtin">int64</span> <span class="token punctuation">{</span>
	<span class="token keyword">var</span> sum <span class="token builtin">int64</span> <span class="token operator">=</span> <span class="token number">0</span>
	<span class="token keyword">for</span> x <span class="token operator">&gt;</span> <span class="token number">0</span> <span class="token punctuation">{</span>
		a <span class="token operator">:=</span> x <span class="token operator">%</span> <span class="token number">10</span>
		x <span class="token operator">=</span> x <span class="token operator">/</span> <span class="token number">10</span>
		sum <span class="token operator">+=</span> a
	<span class="token punctuation">}</span>
	<span class="token keyword">return</span> sum
<span class="token punctuation">}</span>

<span class="token comment">// 生成int64的随机数放入通道ch1中</span>
<span class="token keyword">func</span> <span class="token function">createRand</span><span class="token punctuation">(</span>ch1 <span class="token keyword">chan</span><span class="token operator">&lt;-</span> <span class="token builtin">int64</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">for</span> <span class="token punctuation">{</span>
		int63 <span class="token operator">:=</span> rand<span class="token punctuation">.</span><span class="token function">Int63</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
		ch1 <span class="token operator">&lt;-</span> int63
		time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">//从通道ch1读取数据，然后计算各个位数之和存入ch2中</span>
<span class="token keyword">func</span> <span class="token function">readRand</span><span class="token punctuation">(</span>ch1 <span class="token operator">&lt;-</span><span class="token keyword">chan</span> <span class="token builtin">int64</span><span class="token punctuation">,</span> ch2 <span class="token keyword">chan</span><span class="token operator">&lt;-</span> <span class="token builtin">int64</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">for</span> <span class="token punctuation">{</span>
		value <span class="token operator">:=</span> <span class="token operator">&lt;-</span>ch1
		number <span class="token operator">:=</span> <span class="token function">randNumber</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span>
		ch2 <span class="token operator">&lt;-</span> number
		fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>value<span class="token punctuation">,</span> number<span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">var</span> jobChan <span class="token operator">=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int64</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span>
	<span class="token keyword">var</span> resultChan <span class="token operator">=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int64</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span>
	wg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">25</span><span class="token punctuation">)</span>
	<span class="token keyword">go</span> <span class="token function">createRand</span><span class="token punctuation">(</span>jobChan<span class="token punctuation">)</span>

	<span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">24</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>
		<span class="token keyword">go</span> <span class="token function">readRand</span><span class="token punctuation">(</span>jobChan<span class="token punctuation">,</span> resultChan<span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
	<span class="token comment">//循环打印数随机生成树的各位之和</span>
	<span class="token keyword">for</span> value <span class="token operator">:=</span> <span class="token keyword">range</span> resultChan <span class="token punctuation">{</span>
		fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
	wg<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br><span class="line-number">53</span><br><span class="line-number">54</span><br><span class="line-number">55</span><br><span class="line-number">56</span><br></div></div><h3 id="_5-9-select多路复用"><a href="#_5-9-select多路复用" class="header-anchor">#</a> 5.9 select多路复用</h3> <div class="custom-block tip"><p class="custom-block-title">select的多路复用说明</p> <ul><li>1、解决如果一个<code>channel</code>中没有事件发过来,程序会立即阻塞,无法接收到第二个<code>channel</code>中的事件</li> <li>2、和<code>switch</code>语句稍微有点相似，也会有几个<code>case</code>和最后的<code>default</code>选择支</li> <li>3、每一个<code>case</code>代表一个通信操作(在某个<code>channel</code>上进行发送或者接收)并且会包含一些语句组成的一个语句块,多个<code>case</code>会选一个能执行的</li> <li>4、<code>default</code>会默认执行,因此可以作为轮询<code>channel</code>来用</li> <li>5、一个接收表达式可能只包含接收表达式自身,或者包含在一个简短的变量声明中</li> <li>6、<code>select</code>会等待<code>case</code>中有能够执行的<code>case</code>时去执行,执行后，其他通信是不会执行</li> <li>7、没有任何<code>case</code>的<code>select</code>会永远等待下去，写作<code>select{}</code></li> <li>8、对一个<code>nil</code>的<code>channel</code>发送和接收操作会永远阻塞</li> <li>9、在<code>select</code>语句中操作<code>nil</code>的<code>channel</code>永远都不会被<code>select</code>到</li></ul></div> <h4 id="_5-9-1-具体格式"><a href="#_5-9-1-具体格式" class="header-anchor">#</a> 5.9.1 具体格式</h4> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">select</span><span class="token punctuation">{</span>
    <span class="token keyword">case</span> <span class="token operator">&lt;-</span>ch1<span class="token punctuation">:</span>
        <span class="token operator">...</span>
    <span class="token keyword">case</span> data <span class="token operator">:=</span> <span class="token operator">&lt;-</span>ch2<span class="token punctuation">:</span>
        <span class="token operator">...</span>
    <span class="token keyword">case</span> ch3<span class="token operator">&lt;-</span>data<span class="token punctuation">:</span>
        <span class="token operator">...</span>
    <span class="token keyword">default</span><span class="token punctuation">:</span>
        默认操作
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><h4 id="_5-9-2-select举例"><a href="#_5-9-2-select举例" class="header-anchor">#</a> 5.9.2 select举例</h4> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
	<span class="token string">&quot;fmt&quot;</span>
<span class="token punctuation">)</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token comment">//定义一个缓冲通道,大小是1</span>
	ch <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span>
	<span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">10</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>
		<span class="token comment">//select多路复用</span>
		<span class="token comment">//1.第一个case会阻塞,第二个case执行0发送到channel</span>
		<span class="token comment">//2.第一个case会执行打印channel中的值,第二个case会阻塞</span>
		<span class="token comment">//3.第一个阻塞,第二个执行2会发送到channel 交叉执行下去</span>
		<span class="token keyword">select</span> <span class="token punctuation">{</span>
		<span class="token keyword">case</span> x <span class="token operator">:=</span> <span class="token operator">&lt;-</span>ch<span class="token punctuation">:</span>
			fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span>
		<span class="token keyword">case</span> ch <span class="token operator">&lt;-</span> i<span class="token punctuation">:</span>
	    <span class="token keyword">default</span><span class="token punctuation">:</span>
			fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">&quot;case条件都不满足时，执行的操作&quot;</span><span class="token punctuation">)</span>
		<span class="token punctuation">}</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br></div></div><p>输出结果</p> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token number">0</span>
<span class="token number">2</span>
<span class="token number">4</span>
<span class="token number">6</span>
<span class="token number">8</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><h3 id="_5-10-channel总结"><a href="#_5-10-channel总结" class="header-anchor">#</a> 5.10 channel总结</h3> <ul><li>1 <code>channel</code>是用于<code>goroutine</code>传递消息的</li> <li>2 通道 <code>channel</code>，每个都有相关联的数据类型，<code>nil chan</code>无法使用，类似于<code>nil map</code>，不能直接储存键值对</li> <li>3 使用通道传递数据：<code>&lt;-</code>，根据箭头方法进行数据传递</li> <li>4 阻塞：</li> <li>4.1 发送数据：<code>chan &lt;- data</code>，阻塞的，直到另一条<code>goroutine</code>读取数据来解除阻塞</li> <li>4.2 读取数据：<code>data &lt;- chan</code>，阻塞的，直到另一条<code>goroutine</code>写出数据来解除阻塞</li> <li>5 本身<code>channel</code>就是同步的，意味着同一时间，只能有一条<code>goroutine</code>来操作</li> <li>6 通道是<code>goroutine</code>之间的连接，所以通道的发送和接收必须处在不同的<code>goroutine</code>中。</li> <li>7 <code>channel</code>常见的异常总结，如下图：</li></ul> <p><a data-fancybox="" title="channelException" href="/img/goImage/channelException.png"><img src="/img/goImage/channelException.png" alt="channelException"></a></p> <h2 id="_6-sync-once"><a href="#_6-sync-once" class="header-anchor">#</a> 6 sync.Once</h2> <p>sync.Once表示只执行一次函数。要做到这点，就需要如下两点</p> <div class="custom-block tip"><p class="custom-block-title">sync.Once简介</p> <ul><li>1）计数器，统计函数执行次数；</li> <li>2）线程安全，保障在多个<code>goroutine</code>情况下，函数仍然只执行一次，比如锁。</li></ul></div> <h3 id="_6-1-sync-once源码"><a href="#_6-1-sync-once源码" class="header-anchor">#</a> 6.1 sync.Once源码</h3> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">import</span> <span class="token punctuation">(</span>
   <span class="token string">&quot;sync/atomic&quot;</span>
<span class="token punctuation">)</span>

<span class="token comment">// Once is an object that will perform exactly one action.</span>
<span class="token keyword">type</span> Once <span class="token keyword">struct</span> <span class="token punctuation">{</span>
   m    Mutex
   done <span class="token builtin">uint32</span>
<span class="token punctuation">}</span>
<span class="token comment">// Do calls the function f if and only if Do is being called for the</span>
<span class="token comment">// first time for this instance of Once. In other words, given</span>
<span class="token comment">//     var once Once</span>
<span class="token comment">// if once.Do(f) is called multiple times, only the first call will invoke f,</span>
<span class="token comment">// even if f has a different value in each invocation. A new instance of</span>
<span class="token comment">// Once is required for each function to execute.</span>
<span class="token comment">//</span>
<span class="token comment">// Do is intended for initialization that must be run exactly once. Since f</span>
<span class="token comment">// is niladic, it may be necessary to use a function literal to capture the</span>
<span class="token comment">// arguments to a function to be invoked by Do:</span>
<span class="token comment">//     config.once.Do(func() { config.init(filename) })</span>
<span class="token comment">//</span>
<span class="token comment">// Because no call to Do returns until the one call to f returns, if f causes</span>
<span class="token comment">// Do to be called, it will deadlock.</span>
<span class="token comment">//</span>
<span class="token comment">// If f panics, Do considers it to have returned; future calls of Do return</span>
<span class="token comment">// without calling f.</span>
<span class="token comment">//</span>
<span class="token keyword">func</span> <span class="token punctuation">(</span>o <span class="token operator">*</span>Once<span class="token punctuation">)</span> <span class="token function">Do</span><span class="token punctuation">(</span>f <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
   <span class="token keyword">if</span> atomic<span class="token punctuation">.</span><span class="token function">LoadUint32</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>o<span class="token punctuation">.</span>done<span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">1</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span>
   <span class="token punctuation">}</span>
   <span class="token comment">// Slow-path.</span>
   o<span class="token punctuation">.</span>m<span class="token punctuation">.</span><span class="token function">Lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
   <span class="token keyword">defer</span> o<span class="token punctuation">.</span>m<span class="token punctuation">.</span><span class="token function">Unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
   <span class="token keyword">if</span> o<span class="token punctuation">.</span>done <span class="token operator">==</span> <span class="token number">0</span> <span class="token punctuation">{</span>
      <span class="token keyword">defer</span> atomic<span class="token punctuation">.</span><span class="token function">StoreUint32</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>o<span class="token punctuation">.</span>done<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span>
      <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
   <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br></div></div><h3 id="_6-2-do方法"><a href="#_6-2-do方法" class="header-anchor">#</a> 6.2 Do方法</h3> <blockquote><p>Do方法相当简单，但是也是有可以学习的地方。如果我写一般就直接先加锁，然后比较函数执行次数。而这里用原子操作可以提高性能，学习了。
一些标志位可以通过原子操作表示，避免加锁，提高性能。Do方法特点如下</p></blockquote> <ul><li>首先原子load函数执行次数，如果已经执行过了，就return</li> <li>lock</li> <li>执行函数</li> <li>原子store函数执行次数1</li> <li>unlock</li></ul> <h3 id="_6-3-举例一"><a href="#_6-3-举例一" class="header-anchor">#</a> 6.3 举例一</h3> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
	<span class="token string">&quot;fmt&quot;</span>
	<span class="token string">&quot;sync&quot;</span>
	<span class="token string">&quot;time&quot;</span>
<span class="token punctuation">)</span>

<span class="token keyword">var</span> once sync<span class="token punctuation">.</span>Once
<span class="token keyword">var</span> onceBody <span class="token operator">=</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">&quot;Only once&quot;</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>

	<span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">10</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>
		<span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span>i <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
			once<span class="token punctuation">.</span><span class="token function">Do</span><span class="token punctuation">(</span>onceBody<span class="token punctuation">)</span>
			fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">&quot;i=&quot;</span><span class="token punctuation">,</span>i<span class="token punctuation">)</span>
		<span class="token punctuation">}</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
	time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span> <span class="token comment">//睡眠1s用于执行go程，注意睡眠时间不能太短</span>
<span class="token punctuation">}</span>
<span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span>输出结果<span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span>
Only once
i<span class="token operator">=</span> <span class="token number">0</span>
i<span class="token operator">=</span> <span class="token number">1</span>
i<span class="token operator">=</span> <span class="token number">2</span>
i<span class="token operator">=</span> <span class="token number">4</span>
i<span class="token operator">=</span> <span class="token number">5</span>
i<span class="token operator">=</span> <span class="token number">6</span>
i<span class="token operator">=</span> <span class="token number">3</span>
i<span class="token operator">=</span> <span class="token number">7</span>
i<span class="token operator">=</span> <span class="token number">8</span>
i<span class="token operator">=</span> <span class="token number">9</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br></div></div><p><strong>从输出结果可以看出,尽管for循环每次都会调用<code>once.Do()</code>方法,但是函数<code>onceBody()</code>却只会被执行一次</strong></p> <h2 id="_7-sync-map"><a href="#_7-sync-map" class="header-anchor">#</a> 7 sync.Map</h2> <blockquote><p>go中线程安全的Map就是<code>sync.Map</code>。在单协程访问时我们使用<code>map</code>就可以了，但是在多个协程并发访问时要使用协程安全的<code>sync.Map</code>，原生的<code>map</code>在并发读写时会<code>panic</code>严重错误。
<code>sync.Map</code>追求更好的性能和稳定性,实现思路主要面向多读少写的情况，所以写性能其实比较一般。<a href="https://blog.csdn.net/jiankunking/article/details/78808978" target="_blank" rel="noopener noreferrer">sync.Map源码解读<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></p></blockquote> <h3 id="_7-1-sync-map的的整体结构"><a href="#_7-1-sync-map的的整体结构" class="header-anchor">#</a> 7.1 sync.Map的的整体结构</h3> <p><a data-fancybox="" title="sync.Map的API" href="/img/goImage/syncmap.png"><img src="/img/goImage/syncmap.png" alt="sync.Map的API"></a></p> <h3 id="_7-2-sync-map的结构体说明"><a href="#_7-2-sync-map的结构体说明" class="header-anchor">#</a> 7.2 sync.Map的结构体说明</h3> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">type</span> Map <span class="token keyword">struct</span> <span class="token punctuation">{</span>
    mu Mutex
    read atomic<span class="token punctuation">.</span>Value <span class="token comment">// readOnly</span>
    dirty <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token operator">*</span>entry
    misses <span class="token builtin">int</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><div class="custom-block tip"><p class="custom-block-title">sync.Map属性说明</p> <ul><li><code>mu</code> 是<code>map</code>内部持有的锁，来同步协程之间的操作。</li> <li><code>read</code> 包含一部分<code>map</code>的协程安全的信息（无论有没有加锁）。<code>read</code>因为是一个原子变量，本身就是协程安全的。<code>read</code>中存储的<code>entry</code>可以在没有<code>mu</code>的情况下并发地更新，但是需要将更新之前要被删除的<code>entry</code>复制到<code>dirty</code>中，并在可以在持有<code>mu</code>的情况下恢复。</li> <li><code>dirty</code> 同样保存了一部分<code>map</code>的信息（操作的时候需要<code>mu</code>协同的部分）为了确保<code>dirty</code>可以快速升级为<code>read map</code>，它还包括<code>read map</code>中所有未删除的条目。</li> <li>被删除的<code>entry</code>不存储在<code>dirty</code>中。<code>clean map</code>中的被删除的<code>entry</code>必须是可恢复的，在新值覆盖前存放到<code>dirty</code>中。</li> <li><code>missed</code>是记录没命中<code>read</code>的次数。</li> <li><code>entry</code>保存的是一个指针的值，指向数据，但是有两个特殊值<code>nil&amp;expunged</code>,<code>nil</code>表示在<code>read</code>中被删除了，但是<code>dirty</code>中还在，所以能直接更新值，<code>expunged</code>代表数据在<code>ditry</code>中已经被删除了，更新值的时候要先把这个<code>entry</code>复制到<code>dirty</code>。</li></ul></div> <h3 id="_7-3-sync-map常用操作函数"><a href="#_7-3-sync-map常用操作函数" class="header-anchor">#</a> 7.3 sync.Map常用操作函数</h3> <ul><li><code>Store</code>存 <code>key,value</code></li> <li><code>LoadOrStore</code>取<code>&amp;</code>存-具体看代码</li> <li><code>Load</code>取<code>key</code>对应的<code>value</code></li> <li><code>Range</code>遍历所有的<code>key</code>,<code>value</code></li> <li><code>Delete</code>删除<code>key</code>,及其<code>value</code></li></ul> <h3 id="_7-4-操作函数源码解读"><a href="#_7-4-操作函数源码解读" class="header-anchor">#</a> 7.4 操作函数源码解读</h3> <blockquote><p>关于操作函数这里就仅介绍下写入、读取、删除三个核心函数：</p></blockquote> <h4 id="写入函数"><a href="#写入函数" class="header-anchor">#</a> 写入函数</h4> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token comment">// Store sets the value for a key.</span>
<span class="token keyword">func</span> <span class="token punctuation">(</span>m <span class="token operator">*</span>Map<span class="token punctuation">)</span> <span class="token function">Store</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> value <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 先检查是否已经存在该元素，存在的话，直接通过read中的entry来更新值；</span>
    read<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> m<span class="token punctuation">.</span>read<span class="token punctuation">.</span><span class="token function">Load</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token punctuation">(</span>readOnly<span class="token punctuation">)</span>
    <span class="token keyword">if</span> e<span class="token punctuation">,</span> ok <span class="token operator">:=</span> read<span class="token punctuation">.</span>m<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">;</span> ok <span class="token operator">&amp;&amp;</span> e<span class="token punctuation">.</span><span class="token function">tryStore</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>value<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// tryStore 通过atomic的cas来解决冲突，如果发现数据被置为expung，tryStore不写入数据，直接返回false</span>
        <span class="token keyword">return</span>
    <span class="token punctuation">}</span>
<span class="token comment">/**在read中不存在，先上锁：
1、double check发现read中存在的话，entry为expunged，尝试把expunged替换成nil，如果entry.p==expunged则复制到dirty中，再写入值；否则不用替换直接写入值。
2、dirty中存在：直接更新
3、dirty中不存在：如果dirty为空，那么需要将read复制到dirty中，最后再把新值写入到dirty中。复制的时候调用的是dirtyLocked()，在复制到dirty的时候，read中为nil的元素，会更新为expunged，并且不复制到dirty中。
**/</span>
    m<span class="token punctuation">.</span>mu<span class="token punctuation">.</span><span class="token function">Lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    read<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">=</span> m<span class="token punctuation">.</span>read<span class="token punctuation">.</span><span class="token function">Load</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token punctuation">(</span>readOnly<span class="token punctuation">)</span>
    <span class="token keyword">if</span> e<span class="token punctuation">,</span> ok <span class="token operator">:=</span> read<span class="token punctuation">.</span>m<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">;</span> ok <span class="token punctuation">{</span>
        <span class="token keyword">if</span> e<span class="token punctuation">.</span><span class="token function">unexpungeLocked</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            m<span class="token punctuation">.</span>dirty<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> e
        <span class="token punctuation">}</span>
        e<span class="token punctuation">.</span><span class="token function">storeLocked</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>value<span class="token punctuation">)</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> e<span class="token punctuation">,</span> ok <span class="token operator">:=</span> m<span class="token punctuation">.</span>dirty<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">;</span> ok <span class="token punctuation">{</span>
        e<span class="token punctuation">.</span><span class="token function">storeLocked</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>value<span class="token punctuation">)</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token operator">!</span>read<span class="token punctuation">.</span>amended <span class="token punctuation">{</span>
            m<span class="token punctuation">.</span><span class="token function">dirtyLocked</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            m<span class="token punctuation">.</span>read<span class="token punctuation">.</span><span class="token function">Store</span><span class="token punctuation">(</span>readOnly<span class="token punctuation">{</span>m<span class="token punctuation">:</span> read<span class="token punctuation">.</span>m<span class="token punctuation">,</span> amended<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span>
        m<span class="token punctuation">.</span>dirty<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">newEntry</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    m<span class="token punctuation">.</span>mu<span class="token punctuation">.</span><span class="token function">Unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br></div></div><h4 id="读取函数"><a href="#读取函数" class="header-anchor">#</a> 读取函数</h4> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">func</span> <span class="token punctuation">(</span>m <span class="token operator">*</span>Map<span class="token punctuation">)</span> <span class="token function">Load</span><span class="token punctuation">(</span>key <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">(</span>value <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> ok <span class="token builtin">bool</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// 1、先读read</span>
    read<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> m<span class="token punctuation">.</span>read<span class="token punctuation">.</span><span class="token function">Load</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token punctuation">(</span>readOnly<span class="token punctuation">)</span>
    e<span class="token punctuation">,</span> ok <span class="token operator">:=</span> read<span class="token punctuation">.</span>m<span class="token punctuation">[</span>key<span class="token punctuation">]</span>
    <span class="token keyword">if</span> <span class="token operator">!</span>ok <span class="token operator">&amp;&amp;</span> read<span class="token punctuation">.</span>amended <span class="token punctuation">{</span>
<span class="token comment">// 2、如果read中没有，则加锁读dirty</span>
        m<span class="token punctuation">.</span>mu<span class="token punctuation">.</span><span class="token function">Lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        read<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">=</span> m<span class="token punctuation">.</span>read<span class="token punctuation">.</span><span class="token function">Load</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token punctuation">(</span>readOnly<span class="token punctuation">)</span>
        e<span class="token punctuation">,</span> ok <span class="token operator">=</span> read<span class="token punctuation">.</span>m<span class="token punctuation">[</span>key<span class="token punctuation">]</span>
        <span class="token keyword">if</span> <span class="token operator">!</span>ok <span class="token operator">&amp;&amp;</span> read<span class="token punctuation">.</span>amended <span class="token punctuation">{</span>
            e<span class="token punctuation">,</span> ok <span class="token operator">=</span> m<span class="token punctuation">.</span>dirty<span class="token punctuation">[</span>key<span class="token punctuation">]</span>
<span class="token comment">// 调用missLocked，递增misses，如果misses&gt;len(dirty)，那么把dirty提升为read，清空原来的dirty</span>
            m<span class="token punctuation">.</span><span class="token function">missLocked</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span>
        m<span class="token punctuation">.</span>mu<span class="token punctuation">.</span><span class="token function">Unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">if</span> <span class="token operator">!</span>ok <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token boolean">nil</span><span class="token punctuation">,</span> <span class="token boolean">false</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">return</span> e<span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br></div></div><h4 id="删除"><a href="#删除" class="header-anchor">#</a> 删除</h4> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token comment">// Delete deletes the value for a key.</span>
<span class="token keyword">func</span> <span class="token punctuation">(</span>m <span class="token operator">*</span>Map<span class="token punctuation">)</span> <span class="token function">Delete</span><span class="token punctuation">(</span>key <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// 检查read中是否存在</span>
    read<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> m<span class="token punctuation">.</span>read<span class="token punctuation">.</span><span class="token function">Load</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token punctuation">(</span>readOnly<span class="token punctuation">)</span> 
    e<span class="token punctuation">,</span> ok <span class="token operator">:=</span> read<span class="token punctuation">.</span>m<span class="token punctuation">[</span>key<span class="token punctuation">]</span>
    <span class="token keyword">if</span> <span class="token operator">!</span>ok <span class="token operator">&amp;&amp;</span> read<span class="token punctuation">.</span>amended <span class="token punctuation">{</span>
        m<span class="token punctuation">.</span>mu<span class="token punctuation">.</span><span class="token function">Lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 上锁</span>
        read<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">=</span> m<span class="token punctuation">.</span>read<span class="token punctuation">.</span><span class="token function">Load</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token punctuation">(</span>readOnly<span class="token punctuation">)</span>
        e<span class="token punctuation">,</span> ok <span class="token operator">=</span> read<span class="token punctuation">.</span>m<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token comment">// 双检查</span>
        <span class="token keyword">if</span> <span class="token operator">!</span>ok <span class="token operator">&amp;&amp;</span> read<span class="token punctuation">.</span>amended <span class="token punctuation">{</span>
            <span class="token function">delete</span><span class="token punctuation">(</span>m<span class="token punctuation">.</span>dirty<span class="token punctuation">,</span> key<span class="token punctuation">)</span> <span class="token comment">// 如果没有直接，删除dirty中的数据</span>
        <span class="token punctuation">}</span>
        m<span class="token punctuation">.</span>mu<span class="token punctuation">.</span><span class="token function">Unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">if</span> ok <span class="token punctuation">{</span>
        e<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 如果存在，read中的pointer置为nil，并且删除dirty数据</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br></div></div><h3 id="_7-5-sync-map举例"><a href="#_7-5-sync-map举例" class="header-anchor">#</a> 7.5 sync.Map举例</h3> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main
 
<span class="token keyword">import</span> <span class="token punctuation">(</span>
    <span class="token string">&quot;fmt&quot;</span>
    <span class="token string">&quot;sync&quot;</span>
<span class="token punctuation">)</span>
 
<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">var</span> m sync<span class="token punctuation">.</span>Map
 
    <span class="token comment">//Store</span>
    m<span class="token punctuation">.</span><span class="token function">Store</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token string">&quot;a&quot;</span><span class="token punctuation">)</span>
    m<span class="token punctuation">.</span><span class="token function">Store</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token string">&quot;b&quot;</span><span class="token punctuation">)</span>
 
    <span class="token comment">//LoadOrStore</span>
    <span class="token comment">//若key不存在，则存入key和value，返回false和输入的value</span>
    v<span class="token punctuation">,</span>ok <span class="token operator">:=</span> m<span class="token punctuation">.</span><span class="token function">LoadOrStore</span><span class="token punctuation">(</span><span class="token string">&quot;1&quot;</span><span class="token punctuation">,</span><span class="token string">&quot;aaa&quot;</span><span class="token punctuation">)</span>
    fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>ok<span class="token punctuation">,</span>v<span class="token punctuation">)</span> <span class="token comment">//false aaa</span>
 
    <span class="token comment">//若key已存在，则返回true和key对应的value，不会修改原来的value</span>
    v<span class="token punctuation">,</span>ok <span class="token operator">=</span> m<span class="token punctuation">.</span><span class="token function">LoadOrStore</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token string">&quot;aaa&quot;</span><span class="token punctuation">)</span>
    fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>ok<span class="token punctuation">,</span>v<span class="token punctuation">)</span> <span class="token comment">//false aaa</span>
 
    <span class="token comment">//Load</span>
    v<span class="token punctuation">,</span>ok <span class="token operator">=</span> m<span class="token punctuation">.</span><span class="token function">Load</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
    <span class="token keyword">if</span> ok<span class="token punctuation">{</span>
        fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">&quot;it's an existing key,value is &quot;</span><span class="token punctuation">,</span>v<span class="token punctuation">)</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
        fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">&quot;it's an unknown key&quot;</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
 
    <span class="token comment">//Range</span>
    <span class="token comment">//遍历sync.Map, 要求输入一个func作为参数</span>
    f <span class="token operator">:=</span> <span class="token keyword">func</span><span class="token punctuation">(</span>k<span class="token punctuation">,</span> v <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token builtin">bool</span> <span class="token punctuation">{</span>
        <span class="token comment">//这个函数的入参、出参的类型都已经固定，不能修改</span>
        <span class="token comment">//可以在函数体内编写自己的代码，调用map中的k,v</span>
 
            fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>k<span class="token punctuation">,</span>v<span class="token punctuation">)</span>
            <span class="token keyword">return</span> <span class="token boolean">true</span>
        <span class="token punctuation">}</span>
    m<span class="token punctuation">.</span><span class="token function">Range</span><span class="token punctuation">(</span>f<span class="token punctuation">)</span>
 
    <span class="token comment">//Delete</span>
    m<span class="token punctuation">.</span><span class="token function">Delete</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
    fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>m<span class="token punctuation">.</span><span class="token function">Load</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
 
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br></div></div><h2 id="_8-原子操作"><a href="#_8-原子操作" class="header-anchor">#</a> 8 原子操作</h2> <blockquote><p>原子操作即是进行过程中不能被中断的操作。也就是说，针对某个值的原子操作在被进行的过程当中，CPU绝不会再去进行其它的针对该值的操作。无论这些其它的操作是否为原子操作都会是这样。为了实现这样的严谨性，原子操作仅会由一个独立的CPU指令代表和完成。只有这样才能够在并发环境下保证原子操作的绝对安全。
Go语言提供的原子操作都是非侵入式的。它们由标准库代码包<code>sync/atomic</code>中的众多函数代表。我们可以通过调用这些函数对几种简单的类型的值进行原子操作。</p></blockquote> <h3 id="_8-1-原子操作类型"><a href="#_8-1-原子操作类型" class="header-anchor">#</a> 8.1 原子操作类型</h3> <p><code>int32</code>、<code>int64</code>、<code>uint32</code>、<code>uint64</code>、<code>uintptr</code>和<code>unsafe.Pointer</code>类型，共6个</p> <h3 id="_8-2-有哪些原子操作"><a href="#_8-2-有哪些原子操作" class="header-anchor">#</a> 8.2 有哪些原子操作</h3> <p>有5种，即：增或减<code>Add</code>、比较并交换<code>CompareAndSwap</code>、交换<code>Swap</code>、 载入<code>Load</code>、 存储<code>Store</code>。<a href="https://studygolang.com/pkgdoc" target="_blank" rel="noopener noreferrer">sync/atomic包API详解<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></p> <h3 id="_8-3-原子操作示例"><a href="#_8-3-原子操作示例" class="header-anchor">#</a> 8.3 原子操作示例</h3> <blockquote><p>下面例子是用来来比较下互斥锁和原子操作的性能</p></blockquote> <div class="language-go line-numbers-mode"><pre class="language-go"><code><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
	<span class="token string">&quot;fmt&quot;</span>
	<span class="token string">&quot;sync&quot;</span>
	<span class="token string">&quot;sync/atomic&quot;</span>
	<span class="token string">&quot;time&quot;</span>
<span class="token punctuation">)</span>

<span class="token keyword">type</span> Counter <span class="token keyword">interface</span> <span class="token punctuation">{</span>
	<span class="token function">Inc</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token function">Load</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">int64</span>
<span class="token punctuation">}</span>

<span class="token comment">// 普通版</span>
<span class="token keyword">type</span> CommonCounter <span class="token keyword">struct</span> <span class="token punctuation">{</span>
	counter <span class="token builtin">int64</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token punctuation">(</span>c CommonCounter<span class="token punctuation">)</span> <span class="token function">Inc</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	c<span class="token punctuation">.</span>counter<span class="token operator">++</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token punctuation">(</span>c CommonCounter<span class="token punctuation">)</span> <span class="token function">Load</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">int64</span> <span class="token punctuation">{</span>
	<span class="token keyword">return</span> c<span class="token punctuation">.</span>counter
<span class="token punctuation">}</span>

<span class="token comment">// 互斥锁版</span>
<span class="token keyword">type</span> MutexCounter <span class="token keyword">struct</span> <span class="token punctuation">{</span>
	counter <span class="token builtin">int64</span>
	lock    sync<span class="token punctuation">.</span>Mutex
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token punctuation">(</span>m <span class="token operator">*</span>MutexCounter<span class="token punctuation">)</span> <span class="token function">Inc</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	m<span class="token punctuation">.</span>lock<span class="token punctuation">.</span><span class="token function">Lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token keyword">defer</span> m<span class="token punctuation">.</span>lock<span class="token punctuation">.</span><span class="token function">Unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	m<span class="token punctuation">.</span>counter<span class="token operator">++</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token punctuation">(</span>m <span class="token operator">*</span>MutexCounter<span class="token punctuation">)</span> <span class="token function">Load</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">int64</span> <span class="token punctuation">{</span>
	m<span class="token punctuation">.</span>lock<span class="token punctuation">.</span><span class="token function">Lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token keyword">defer</span> m<span class="token punctuation">.</span>lock<span class="token punctuation">.</span><span class="token function">Unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token keyword">return</span> m<span class="token punctuation">.</span>counter
<span class="token punctuation">}</span>

<span class="token comment">// 原子操作版</span>
<span class="token keyword">type</span> AtomicCounter <span class="token keyword">struct</span> <span class="token punctuation">{</span>
	counter <span class="token builtin">int64</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token punctuation">(</span>a <span class="token operator">*</span>AtomicCounter<span class="token punctuation">)</span> <span class="token function">Inc</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	atomic<span class="token punctuation">.</span><span class="token function">AddInt64</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>a<span class="token punctuation">.</span>counter<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token punctuation">(</span>a <span class="token operator">*</span>AtomicCounter<span class="token punctuation">)</span> <span class="token function">Load</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">int64</span> <span class="token punctuation">{</span>
	<span class="token keyword">return</span> atomic<span class="token punctuation">.</span><span class="token function">LoadInt64</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>a<span class="token punctuation">.</span>counter<span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">test</span><span class="token punctuation">(</span>c Counter<span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">var</span> wg sync<span class="token punctuation">.</span>WaitGroup
	start <span class="token operator">:=</span> time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">1000</span><span class="token punctuation">;</span> i<span class="token operator">++</span> <span class="token punctuation">{</span>
		wg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
		<span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
			c<span class="token punctuation">.</span><span class="token function">Inc</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
			wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
		<span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token punctuation">}</span>
	wg<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	end <span class="token operator">:=</span> time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>c<span class="token punctuation">.</span><span class="token function">Load</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> end<span class="token punctuation">.</span><span class="token function">Sub</span><span class="token punctuation">(</span>start<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	c1 <span class="token operator">:=</span> CommonCounter<span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">// 非并发安全</span>
	<span class="token function">test</span><span class="token punctuation">(</span>c1<span class="token punctuation">)</span>
	c2 <span class="token operator">:=</span> MutexCounter<span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">// 使用互斥锁实现并发安全</span>
	<span class="token function">test</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>c2<span class="token punctuation">)</span>
	c3 <span class="token operator">:=</span> AtomicCounter<span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">// 并发安全且比互斥锁效率更高</span>
	<span class="token function">test</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>c3<span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">-</span>输出结果<span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span>
<span class="token number">0</span> <span class="token number">2.0009</span>ms
<span class="token number">1000</span> <span class="token number">1.0007</span>ms
<span class="token number">1000</span> <span class="token number">0</span>s
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br><span class="line-number">53</span><br><span class="line-number">54</span><br><span class="line-number">55</span><br><span class="line-number">56</span><br><span class="line-number">57</span><br><span class="line-number">58</span><br><span class="line-number">59</span><br><span class="line-number">60</span><br><span class="line-number">61</span><br><span class="line-number">62</span><br><span class="line-number">63</span><br><span class="line-number">64</span><br><span class="line-number">65</span><br><span class="line-number">66</span><br><span class="line-number">67</span><br><span class="line-number">68</span><br><span class="line-number">69</span><br><span class="line-number">70</span><br><span class="line-number">71</span><br><span class="line-number">72</span><br><span class="line-number">73</span><br><span class="line-number">74</span><br><span class="line-number">75</span><br><span class="line-number">76</span><br><span class="line-number">77</span><br><span class="line-number">78</span><br><span class="line-number">79</span><br><span class="line-number">80</span><br><span class="line-number">81</span><br><span class="line-number">82</span><br><span class="line-number">83</span><br><span class="line-number">84</span><br><span class="line-number">85</span><br><span class="line-number">86</span><br></div></div></div> <footer class="page-edit"><!----> <!----></footer> <div class="page-nav"><p class="inner"><span class="prev">
      ←
      <a href="/go/learn-go/27 Go语言接口.html" class="prev">
        Go语言接口
      </a></span> <span class="next"><a href="/go/learn-go/29 Go语言文件操作.html">
        Go语言文件操作
      </a>
      →
    </span></p></div> </main></div><div class="global-ui"><!----></div></div>
    <script src="/assets/js/app.1bc80adb.js" defer></script><script src="/assets/js/2.7c0608ab.js" defer></script><script src="/assets/js/82.7dac687f.js" defer></script>
  </body>
</html>
